package com.tiancheng.trade.payment.task.gaocheng;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.tiancheng.commom.pay.gaocheng.GcPayChannel;
import com.tiancheng.commom.pay.gaocheng.GcPayProperties;
import com.tiancheng.commom.pay.gaocheng.req.pay.GcSettleReq;
import com.tiancheng.commom.pay.gaocheng.res.GcBaseResp;
import com.tiancheng.commom.pay.gaocheng.res.GcOrderQueryRes;
import com.tiancheng.trade.commom.core.enums.PaymentChannelEnum;
import com.tiancheng.trade.commom.core.enums.SettleStatusEnum;
import com.tiancheng.trade.commom.core.exception.Asserts;
import com.tiancheng.trade.commom.core.exception.BusinessException;
import com.tiancheng.trade.commom.core.exception.error.BusinessErrorInfoEnum;
import com.tiancheng.trade.commom.core.utils.DateTimeUtil;
import com.tiancheng.trade.commom.core.utils.IdWorkerUtils;
import com.tiancheng.trade.commom.core.utils.JsonUtil;
import com.tiancheng.trade.payment.bo.SettleFlowDetailMerchantBO;
import com.tiancheng.trade.payment.channel.gaocheng.GcPayConfig;
import com.tiancheng.trade.payment.channel.umsshop.UmsShopAllConfig;
import com.tiancheng.trade.payment.channel.umsshop.UmsShopProperties;
import com.tiancheng.trade.payment.channel.umsshop.UmsShopSettleClient;
import com.tiancheng.trade.payment.channel.umsshop.request.UmsShopProcessWithdrawReq;
import com.tiancheng.trade.payment.channel.umsshop.request.UmsShopWithdrawStatusQueryReq;
import com.tiancheng.trade.payment.channel.umsshop.response.UmsShopProcessWithdrawRes;
import com.tiancheng.trade.payment.channel.umsshop.response.UmsShopWithdrawMerchantBalanceQueryRes;
import com.tiancheng.trade.payment.channel.umsshop.response.UmsShopWithdrawStatusQueryRes;
import com.tiancheng.trade.payment.enums.WithdrawalStatusEnum;
import com.tiancheng.trade.payment.model.*;
import com.tiancheng.trade.payment.service.*;
import com.tiancheng.trade.payment.util.helper.PaymentIdHelper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author: kellen
 * @Description: 高诚结算任务
 * @Date: create in 2025/05/22 09:31
 */
@Slf4j
@Component
public class GaoChengSettleTask {
    /**
     * 单个文件明细数不超过1万条
     */
    private static final int FILE_MAX = 9000;
    private static final String LOST_RULE = "缺少清分规则";
    private static final String LOST_MERCHANT = "缺少商户信息或商户企业用户号";
    private static final String LESS_APPR_AMOUNT = "划付轧差为负";
    private static final String LESS_DIST_AMOUNT = "分账轧差为负";
    private static final String SETTLE_ERROR = "ERROR";
    private static final String SETTLE_ING = "ING";
    private static final String SETTLE_SUCCESS = "SUCCESS";
    // 回盘文件查找判断时间
    public static final String SETTLE_TIME = "05:00:00";

    @Resource
    private RedissonClient redissonClient;
    @Resource
    private GcPayConfig gcPayConfig;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private PaymentFlowDetailSettleService paymentFlowDetailSettleService;
    @Resource
    private IPaymentFlowDetailSettleDetailService paymentFlowDetailSettleDetailService;
    @Resource
    private UmsShopSettleClient umsShopSettleClient;
    @Resource
    private IRefundSettleDetailService refundSettleDetailService;
    @Resource
    private IRefundSettleService refundSettleService;
    @Resource
    private IWithdrawalService withdrawalService;
    @Resource
    private PaymentOrderService paymentOrderService;
    public static final String SETTLE_CHANNEL_NAME = PaymentChannelEnum.gao_cheng_nong_hang.getDesc();


    @Scheduled(cron = "0 0 15 * * ?")
    public void doSettle() {
        log.info("{}-> 自动清分任务开始执行：{}", SETTLE_CHANNEL_NAME, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        // 同时刻不允许有多个任务处理相同数据
        RLock lock = redissonClient.getLock("UMS_SHOP_SETTLE_LOCK");
        boolean isLock;
        try {
            isLock = lock.tryLock(30L, TimeUnit.MINUTES);
            if (!isLock) {
                log.error("{}-> 自动清分任务执行结果：锁失败！", SETTLE_CHANNEL_NAME);
                return;
            }
            // 1、查询等待清分商户信息
            List<PaymentFlowDetailSettle> flowDetailSettles = paymentFlowDetailSettleService.waitSettleAccountsFlows(PaymentChannelEnum.gao_cheng_nong_hang.getCode());
            if (CollectionUtils.isEmpty(flowDetailSettles)) {
                log.info("{}-> 自动清分任务执行结果：无应清分数据！", SETTLE_CHANNEL_NAME);
                return;
            }

            Set<String> flowIds = flowDetailSettles.stream().map(PaymentFlowDetailSettle::getFlowId).collect(Collectors.toSet());
            // 待结算流水集合
            List<PaymentFlowDetailSettle> pfdss = paymentFlowDetailSettleService.list(new LambdaQueryWrapper<PaymentFlowDetailSettle>().in(PaymentFlowDetailSettle::getFlowId, flowIds));
            // 待结算流水明细

            // 2、查询支付流水明细
            List<PaymentOrder> paymentOrders = paymentOrderService.list(new LambdaQueryWrapper<PaymentOrder>().in(PaymentOrder::getFlowId, flowIds));
            if (CollectionUtils.isEmpty(paymentOrders)) {
                log.info("{}-> 自动清分任务执行结果：未查询到支付流水信息！", SETTLE_CHANNEL_NAME);
                return;
            }

            // 结算成功订单
            List<String> successFlowIds = new ArrayList<>();
            // 结算失败订单
            List<String> errorFlowIds = new ArrayList<>();

            paymentOrders.stream().collect(Collectors.groupingBy(PaymentOrder::getOrgPaymentChannelId)).forEach((orgPaymentChannelId, orders) -> {
                GcPayChannel payChannel = new GcPayChannel(initGcPayConfig(orgPaymentChannelId));
                String batchNo = PaymentIdHelper.getWithdrawalFlowId(orgPaymentChannelId.toString());
                int position = 1;
                for (PaymentOrder order : orders) {
                    GcSettleReq gcSettleReq = new GcSettleReq();
                    gcSettleReq.setMchNo(payChannel.getConfig().getMchNo());
                    gcSettleReq.setOrderDate(DateTimeUtil.formatter(order.getCreateTime(), DateTimeUtil.YYYYMMDD_INTERVAL));
                    gcSettleReq.setBacthNo(batchNo);
                    gcSettleReq.setOrderNo(order.getFlowId());
                    gcSettleReq.setSyncTotal(orders.size());
                    if (position == 1 && position < orders.size()) {
                        // 1 - 同步开始
                        gcSettleReq.setSyncTag(1);
                    }
                    if (position == orders.size()) {
                        // 2 - 同步结束
                        gcSettleReq.setSyncTag(2);
                    }
                    try {
                        GcBaseResp<String> settle = payChannel.settle(gcSettleReq);
                        successFlowIds.add(gcSettleReq.getOrderNo());
                    } catch (Exception e) {
                        log.error("{}-预结算失败，订单号：{}", SETTLE_CHANNEL_NAME, gcSettleReq.getOrderNo());
                        errorFlowIds.add(gcSettleReq.getOrderNo());
                    }
                    position++;

                }
                log.info("批次结算完成，批次号：{},订单数量：{}", batchNo, orders.size());
            });

            /*if (CollectionUtils.isNotEmpty(successFlowIds)) {
                // 提现成功
                PaymentFlowDetailSettleDetail pfdsd = new PaymentFlowDetailSettleDetail();
                pfdsd.setSettleStatus(SettleStatusEnum.ING.getCode());
                pfdsd.setWithdrawalId(withdrawalFlowId);
                paymentFlowDetailSettleDetailService.update(pfdsd, new LambdaQueryWrapper<PaymentFlowDetailSettleDetail>()
                        .in(PaymentFlowDetailSettleDetail::getId, payMerchantEntry.getValue().stream().map(PaymentFlowDetailSettleDetail::getId).collect(Collectors.toSet())));

                PaymentFlowDetailSettle updatePaySettle = new PaymentFlowDetailSettle();
                updatePaySettle.setSettleStatus(success ? SettleStatusEnum.ING.getCode() : SettleStatusEnum.FAIL.getCode());
                paymentFlowDetailSettleService.update(updatePaySettle, new LambdaQueryWrapper<PaymentFlowDetailSettle>().in(PaymentFlowDetailSettle::getSettleFlowId, payMerchantEntry.getValue().stream().map(PaymentFlowDetailSettleDetail::getSubFlowId).collect(Collectors.toSet())));
                if (hashRefundOrder) {
                    RefundSettleDetail rsd = new RefundSettleDetail();
                    rsd.setSettleStatus(success ? SettleStatusEnum.ING.getCode() : SettleStatusEnum.FAIL.getCode());
                    rsd.setWithdrawalId(withdrawalFlowId);
                    refundSettleDetailService.update(rsd, new LambdaQueryWrapper<RefundSettleDetail>()
                            .in(RefundSettleDetail::getId, refundSettlesMap.get(payMerchantEntry.getKey()).stream().map(RefundSettleDetail::getId).collect(Collectors.toSet())));
                }
                // 保存提现记录
                Withdrawal withdrawal = new Withdrawal();
                withdrawal.setWithdrawalId(withdrawalFlowId);
                withdrawal.setSsn(ssn);
                withdrawal.setMerchantRegId(payMerchantEntry.getKey());
                withdrawal.setMerchantCode(payMerchantEntry.getValue().get(0).getMerchantCode());
                withdrawal.setMerchantName(payMerchantEntry.getValue().get(0).getMerchantName());
                withdrawal.setSubMerchantId(payMerchantEntry.getValue().get(0).getSubMerchantId());
                withdrawal.setEnterpriseCode(payMerchantEntry.getValue().get(0).getSettleMerchantId());
                withdrawal.setAmount(settleSumAmount);
                withdrawal.setFee(0);
                withdrawal.setWithdrawalStatus(success ? WithdrawalStatusEnum.ING.getCode() : WithdrawalStatusEnum.FAIL.getCode());
                withdrawal.setOrgPaymentChannelId(first.get().getOrgPaymentChannelId());
                withdrawal.setPaymentChannel(first.get().getPaymentChannel());
                withdrawal.setCompleteTime(LocalDateTime.now());
                withdrawal.setUpdateTime(LocalDateTime.now());
                withdrawal.setCreateTime(LocalDateTime.now());
                withdrawalService.save(withdrawal);
                if (settleSumAmount == 0) {
                    // 没有执行银商提现的提现记录直接设置为提现成功、
                    withdrawalResult(withdrawal.getWithdrawalId(), "0", DateTimeUtil.formatter(LocalDateTime.now(), DateTimeUtil.bj_pattern), WithdrawalStatusEnum.SUCCESS);
                }
            }*/

        } catch (Exception e) {
            log.error("银联商务新购物车-> 提现自动任务执行失败：", e);
            throw new BusinessException(BusinessErrorInfoEnum.UMS_SETTLE_ERROR, null, "银联商务新购物车-> 提现自动任务执行失败", e);
        } finally {
            if (lock.isLocked()) {
                lock.unlock();
            }
        }
    }


    private String getRemark(String merchantName) {
        String str = StringUtils.isNotEmpty(merchantName) ? merchantName + "分账所得" : "商户分账所得";
        return str.length() >= 20 ? str.substring(0, 19) : str;
    }

    /**
     * 提现状态查询
     * 每天21-23点期间每隔30分钟执行一次
     */
    //@Scheduled(cron = "0 */30 21,22,23 * * ?")
    @Scheduled(cron = "0 */30 17,18,19 * * ?")
    public void queryWithdrawStatus() {
        List<Withdrawal> withdrawals = withdrawalService.list(new LambdaQueryWrapper<Withdrawal>().eq(Withdrawal::getWithdrawalStatus, WithdrawalStatusEnum.ING.getCode())
                .eq(Withdrawal::getPaymentChannel, "umsShop"));
        if (CollectionUtils.isEmpty(withdrawals)) {
            log.info("未查询到提现记录，返回！");
            return;
        }
    }

    /**
     * 提现接口处理
     *
     * @param withdrawalId              提现id
     * @param fee                       手续费
     * @param transTimeStr              提现时间：yyyy-MM-dd HH:mm:ss
     * @param finalWithdrawalStatusEnum 提现状态
     */
    private void withdrawalResult(String withdrawalId, String fee, String transTimeStr, WithdrawalStatusEnum finalWithdrawalStatusEnum) {
        // 更新提现记录状态，处理后续流程
        Withdrawal updWithdrawal = new Withdrawal();
        updWithdrawal.setWithdrawalStatus(finalWithdrawalStatusEnum.getCode());
        if (StringUtils.isNotEmpty(fee)) {
            updWithdrawal.setFee(Integer.parseInt(fee));
        }
        LocalDateTime transTime = null;
        if (StringUtils.isNotEmpty(transTimeStr)) {
            transTime = DateTimeUtil.string2LocalDateTime(transTimeStr, DateTimeUtil.bj_pattern);
            updWithdrawal.setCompleteTime(transTime);
        }
        updWithdrawal.setUpdateTime(LocalDateTime.now());
        withdrawalService.update(updWithdrawal, new LambdaQueryWrapper<Withdrawal>().eq(Withdrawal::getWithdrawalId, withdrawalId));
        paymentFlowDetailSettleService.withdrawResult(withdrawalId, finalWithdrawalStatusEnum, transTime);
    }

    /**
     * 汇总当前等待轧差的退款结算单
     */
    private Map<Long, List<RefundSettleDetail>> queryRefundOrderSettleDetails() {
        long size = 1000L; // 每批次查询起始id
        long current = 1;// 当前页
        Map<Long, List<RefundSettleDetail>> merchantRefundSettleDetailsMap = new HashMap<>();
        List<RefundSettle> list;
        do {
            // 3.1、抓取当日应清分子订单和滞留子订单，每次抓取1000条
            IPage<RefundSettle> refundSettleIPage = refundSettleService.listWaitSettles(LocalDate.now(), PaymentChannelEnum.UMS_SHOP.getCode(), current, size);
            list = refundSettleIPage.getRecords();
            if (CollectionUtils.isEmpty(list)) {
                log.info("未查询到待结算订单或待结算订单处理完毕！");
                break;
            }
            // 支付流水清分明细项
            List<RefundSettleDetail> refundSettleDetails = refundSettleDetailService.queryBySettleIds(list.stream().map(RefundSettle::getId).collect(Collectors.toSet()));
            current++;
            // 按商户分组所有的结算明细项
            if (CollectionUtils.isEmpty(refundSettleDetails)) {
                continue;
            }
            // 按商户分组所有的结算明细项
            Map<Long, List<RefundSettleDetail>> merchantSettles = refundSettleDetails.stream().collect(Collectors.groupingBy(RefundSettleDetail::getMerchantRegId));
            if (!merchantSettles.isEmpty()) {
                merchantSettles.forEach((merchantId, refundFlowDetailSettleDetails) -> {
                    if (merchantRefundSettleDetailsMap.containsKey(merchantId) && CollectionUtils.isNotEmpty(merchantRefundSettleDetailsMap.get(merchantId))) {
                        merchantRefundSettleDetailsMap.get(merchantId).addAll(refundFlowDetailSettleDetails);
                    } else {
                        merchantRefundSettleDetailsMap.put(merchantId, refundFlowDetailSettleDetails);
                    }
                });
            }
        } while (list.size() > 999);
        return merchantRefundSettleDetailsMap;
    }

    private GcPayProperties initGcPayConfig(Long orgPaymentChannelId) {
        return gcPayConfig.getByOrgPayChannelId(orgPaymentChannelId);
    }

}
